;-------------------------------------------------------------------------------
format ELF64 executable 3
entry start

SDL_OPENGL = 0x04000000
SDL_NOFRAME = 0x02000000
SDL_KEYDOWN = 0x300

GL_FRAGMENT_SHADER = 0x8B30

DEBUG = 0

macro emit [param] {param}
;-------------------------------------------------------------------------------
include 'import64.inc'
interpreter '/lib64/ld-linux-x86-64.so.2'
needed 'libdl.so.2'
import dlopen,dlsym
;-------------------------------------------------------------------------------
segment readable writeable executable

start:
    sub rsp,2048
    ; define local variables
    emit < event equ rsp+1024 >,< log equ rsp >
    mov r13,[dlsym]
if DEBUG = 1
    ; load libc
    emit < mov rdi,g_str_libc >,< mov esi,1 >,< call [dlopen] >,< mov [g_libc],rax >
    emit < mov rdi,[g_libc] >,< mov rsi,g_str_printf >,< call r13 >,< mov [printf],rax >
end if
    ; load SDL library
    emit < mov rdi,g_str_libsdl >,< mov esi,1 >,< call [dlopen] >,< mov r12,rax >
    ; call SDL_SetVideoMode
    emit < mov rdi,r12 >,< mov rsi,g_str_SDL_SetVideoMode >,< call r13 >
    emit < mov edi,1280 >,< mov esi,720 >,< xor edx,edx >,< mov ecx,SDL_OPENGL+SDL_NOFRAME >,< call rax >
    ; load SDL_GL_GetProcAddress
    emit < mov rdi,r12 >,< mov rsi,g_str_SDL_GL_GetProcAddress >,< call r13 >,< mov rbx,rax >
    ; create program
    emit < mov rdi,g_str_glCreateShaderProgramv >,< call rbx >
    emit < mov edi,GL_FRAGMENT_SHADER >,< mov esi,1 >,< mov rdx,g_src_fs_ptr >,< call rax >,< mov r15d,eax >
    ; use program
    emit < mov rdi,g_str_glUseProgram >,< call rbx >
    emit < mov edi,r15d >,< call rax >
if DEBUG = 1
    ; print GLSL info log
    emit < mov rdi,g_str_glGetProgramInfoLog >,< call rbx >
    emit < mov edi,r15d>,<mov esi,1024 >,< xor edx,edx >,< lea rcx,[log] >,< call rax >
    emit < mov rdi,g_str_log >,< lea rsi,[log] >,< call [printf] >
end if
      .main_loop:
    ; process window events
    emit < mov rdi,r12 >,< mov rsi,g_str_SDL_PollEvent >,< call r13 >
    emit < lea rdi,[event] >,< call rax >
    cmp dword [event],SDL_KEYDOWN
    je .main_loop_end
    ; get time
    emit < mov rdi,r12 >,< mov rsi,g_str_SDL_GetTicks >,< call r13 >,< call rax >,< mov r14d,eax >
    emit < mov rdi,g_str_glTexCoord1f >,< call rbx >
    emit < cvtsi2ss xmm0,r14d >,< call rax >
    ; draw
    emit < mov rdi,g_str_glRecti >,< call rbx >
    emit < mov edi,-1 >,< mov esi,-1 >,< mov edx,1 >,< mov ecx,1 >,< call rax >
    ; swap
    emit < mov rdi,r12 >,< mov rsi,g_str_SDL_GL_SwapBuffers >,< call r13 >,< call rax >
    jmp .main_loop
      .main_loop_end:
    ; SDL_Quit
    emit < mov rdi,r12 >,< mov rsi,g_str_SDL_Quit >,< call r13 >,< call rax >
    ; terminate process
    emit < mov eax,60 >,< xor edi,edi >,syscall
;-------------------------------------------------------------------------------
align 1
g_src_fs:
db  '#version 150 compatibility',10
db  'vec4 mod289(vec4 x){'
db      'return x-floor(x*(1./289))*289;'
db  '}'
db  'vec4 permute(vec4 x){'
db      'return mod289(((x*34)+1)*x);'
db  '}'
db  'vec4 taylorInvSqrt(vec4 r){'
db      'return 1.79284291400159-.85373472095314*r;'
db  '}'
db  'float snoise(vec3 v){'
db      'vec2 C=vec2(1./6,1./3);'
db      'vec4 D=vec4(0,.5,1,2);'
db      'vec3 i=floor(v+dot(v,C.yyy));'
db      'vec3 x0=v-i+dot(i,C.xxx);'
db      'vec3 g=step(x0.yzx,x0.xyz);'
db      'vec3 l=1-g;'
db      'vec3 i1=min(g.xyz,l.zxy);'
db      'vec3 i2=max(g.xyz,l.zxy);'
db      'vec3 x1=x0-i1+C.xxx;'
db      'vec3 x2=x0-i2+C.yyy;'
db      'vec3 x3=x0-D.yyy;'
db      'i=i-floor(i*(1./289))*289;'
db      'vec4 p=permute(permute(permute(i.z+vec4(0,i1.z,i2.z,1))+i.y+vec4(0,i1.y,i2.y,1))+i.x+vec4(0,i1.x,i2.x,1));'
db      'float n_=.142857142857;'
db      'vec3 ns=n_*D.wyz-D.xzx;'
db      'vec4 j=p-49.*floor(p*ns.z*ns.z);'
db      'vec4 x_=floor(j*ns.z);'
db      'vec4 y_=floor(j-7*x_);'
db      'vec4 x=x_*ns.x+ns.yyyy;'
db      'vec4 y=y_*ns.x+ns.yyyy;'
db      'vec4 h=1.-abs(x)-abs(y);'
db      'vec4 b0=vec4(x.xy,y.xy);'
db      'vec4 b1=vec4(x.zw,y.zw);'
db      'vec4 s0=floor(b0)*2+1;'
db      'vec4 s1=floor(b1)*2+1;'
db      'vec4 sh=-step(h,vec4(0));'
db      'vec4 a0=b0.xzyw+s0.xzyw*sh.xxyy;'
db      'vec4 a1=b1.xzyw+s1.xzyw*sh.zzww;'
db      'vec3 p0=vec3(a0.xy,h.x);'
db      'vec3 p1=vec3(a0.zw,h.y);'
db      'vec3 p2=vec3(a1.xy,h.z);'
db      'vec3 p3=vec3(a1.zw,h.w);'
db      'vec4 norm=taylorInvSqrt(vec4(dot(p0,p0),dot(p1,p1),dot(p2,p2),dot(p3,p3)));'
db      'p0*=norm.x;'
db      'p1*=norm.y;'
db      'p2*=norm.z;'
db      'p3*=norm.w;'
db      'vec4 m=max(.6-vec4(dot(x0,x0),dot(x1,x1),dot(x2,x2),dot(x3,x3)),0);'
db      'm=m*m;'
db      'return 42*dot(m*m,vec4(dot(p0,x0),dot(p1,x1),dot(p2,x2),dot(p3,x3)));'
db  '}'
db  'float fbm(vec3 p){'
db      'return '
db          '.5*snoise(p)+'
db          '.25*snoise(2*p)+'
db          '.125*snoise(4*p)+'
db          '.0625*snoise(8*p);'
db  '}'
db  'float t;'
db  'float box(vec3 p,vec3 b,float r){'
db      'return length(max(abs(p)-b,0.))-r;'
db  '}'
db  'float sphere(vec3 p,vec3 c,float r){'
db      'return length(p-c)-r;'
db  '}'
db  'float map(vec3 p,out int matID){'
db      'float dist,minDist=100;'
db      'dist=box(p,vec3(1.5,1,1.5),.03);'
db      'if(dist<minDist){'
db          'minDist=dist;'
db          'matID=1;'
db      '}'
db      'dist=sphere(p,vec3(0,1,0),1);'
db      'if(dist<minDist){'
db          'minDist=dist;'
db          'matID=2;'
db      '}'
db      'dist=dot(p,vec3(0,1,0));'
db      'if(dist<minDist){'
db          'minDist=dist;'
db          'matID=3;'
db      '}'
db      'return minDist;'
db  '}'
db  'float is(vec3 ro,vec3 rd,out int matID){'
db      'for(float t=0;t<40;){'
db          'float d=map(ro+rd*t,matID);'
db          'if(d<.001)'
db              'return t;'
db          't+=d;'
db      '}'
db      'matID=-1;'
db      'return -1;'
db  '}'
db  'float shadow(vec3 ro,vec3 rd){'
db      'float r=1;'
db      'int m;'
db      'for(float t=.01;t<10;){'
db          'float d=map(ro+rd*t,m);'
db          'if (d<.0005)'
db              'return 0;'
db          'r=min(r,16*d/t);'
db          't+=d;'
db      '}'
db      'return r;'
db  '}'
db  'vec3 computeNormal(vec3 p){'
db      'int m;'
db      'vec3 e=vec3(.001,0,0);'
db      'return normalize(vec3(map(p+e.xyy,m)-map(p-e.xyy,m),map(p+e.yxy,m)-map(p-e.yxy,m),map(p+e.yyx,m)-map(p-e.yyx,m)));'
db  '}'
db  'vec3 computeBump(vec3 p){'
db      'vec3 b;'
db      'float d=fbm(p);'
db      'b.x=fbm(vec3(p.x+.001,p.y,p.z))-d;'
db      'b.y=fbm(vec3(p.x,p.y+.001,p.z))-d;'
db      'b.z=fbm(vec3(p.x,p.y,p.z+.001))-d;'
db      'return b;'
db  '}'
db  'void main(){'
db      't=gl_TexCoord[0].s*.001;'
db      'vec2 p=-1+2*gl_FragCoord.xy/vec2(1280,720);'
db      'p.x*=1.77;'

db      'vec3 ro=vec3(2*cos(.5*t),3,3*sin(.45*t));'
db      'vec3 z=normalize(vec3(0,.5,0)-ro);'
db      'vec3 x=normalize(cross(vec3(0,1,0),z));'
db      'vec3 y=normalize(cross(z,x));'
db      'vec3 rd=normalize(x*p.x+y*p.y+z*1.5+.25*sin(t));'

db      'vec3 c=vec3(0);'
db      'int matID,m;'
db      'float d=is(ro,rd,matID);'
db      'if(d>0){'
db          'vec3 p=ro+d*rd;'
db          'vec3 n=computeNormal(p);'

db          'float ao,sca=10,totao=0;'
db          'for(int i=0;i<5;++i){'
db              'float hr=.01+.015*float(i*i);'
db              'vec3 aopos=n*hr+p;'
db              'float dd=map(aopos,m);'
db              'ao=hr-dd;'
db              'totao+=ao*sca;'
db              'sca*=.5;'
db          '}'
db          'ao=1-clamp(totao,0,1);'

db          'vec3 l0=normalize(vec3(10,8,-6)-p);'
db          'vec3 l1=normalize(vec3(-12+10*cos(.2*t),19,6+12*sin(.3*t))-p);'
db          'float sh0=shadow(p,l0);'
db          'float sh1=shadow(p,l1);'

db          'vec3 rgb=vec3(1);'
db          'if(matID==2){'
db              'sh0=sh1=1;'
db              'n+=100*computeBump(8*p);'
db              'n=normalize(n);'
db              'float fb=fbm(8*p);'
db              'rgb=vec3(.64,.61,.59)+fb*vec3(.21,.19,.19);'
db          '}'
db          'else if(matID==5){'
db              'float fb=fbm(16*p);'
db              'float m=sin(64*p.z+64*p.x+4*fb);'
db              'm=smoothstep(.3,.5,m)-smoothstep(.5,.7,m);'
db              'rgb=vec3(.59)+fb*vec3(.17,.18,.21)+m*vec3(.15);'
db          '}'

db          'c+=.7*max(.4+.6*dot(n,l0)*sh0,0)*rgb;'
db          'c+=.3*max(.4+.6*dot(n,l1)*sh1,0)*rgb;'
db          'c*=ao;'
db      '}'
db      'gl_FragColor=vec4(c,1);'
db  '}',0

g_str_libsdl db 'libSDL-1.3.so.0',0
g_str_SDL_SetVideoMode db 'SDL_SetVideoMode',0
g_str_SDL_PollEvent db 'SDL_PollEvent',0
g_str_SDL_GL_SwapBuffers db 'SDL_GL_SwapBuffers',0
g_str_SDL_GetTicks db 'SDL_GetTicks',0
g_str_SDL_GL_GetProcAddress db 'SDL_GL_GetProcAddress',0
g_str_glCreateShaderProgramv db 'glCreateShaderProgramv',0
g_str_glRecti db 'glRecti',0
g_str_glUseProgram db 'glUseProgram',0
g_str_glTexCoord1f db 'glTexCoord1f',0
g_str_SDL_Quit db 'SDL_Quit',0
if DEBUG = 1
g_str_libc db 'libc.so.6',0
g_str_printf db 'printf',0
g_str_glGetProgramInfoLog db 'glGetProgramInfoLog',0
g_str_log db 'GLSL info log:',10,'%s',10,0
g_str_d db '%d',10,0
g_str_p db '%p',10,0
end if

align 8
g_src_fs_ptr dq g_src_fs

if DEBUG = 1
g_libc dq 0
printf dq 0
end if
;-------------------------------------------------------------------------------
